﻿using RescueRobotController.Core;
using RescueRobotController.panels;
using RescueRobotController.Panels;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;

namespace RescueRobotController
{
    public partial class MainForm : System.Windows.Forms.Form
    {
        // 面板
        SerialPortTerminalPanel serialPortTerminalPanel = null;
        EmergencyStopPanel emergencyStopPanel = null;
        DataDisplayPanel dataDisplayPanel = null;
        RoboticArmPanel roboticArmPanel = null;
        ChassisPanel chassisPanel = null;
        ChassisJibPanel chassisJibPanel = null;
        CameraPanel cameraPanel = null;
        ImpactWarningPanel impactWarningPanel = null;
        AttitudePanel attitudePanel = null;

        // 主串口
        SerialPort mainSerialPort;

        // AT指令解析器
        ATCommandParser atCommandParser;

        // AT指令发送队列
        ConcurrentQueue<ATCmd> mainSerialATCmdQueue;

        // 线程和任务
        Task mainSerialSendTask;
        bool mainSerialSendTaskRunning = true;
        public MainForm()
        {
            InitializeComponent();
        }

        /* *
         * 创建一个新面板的流程：
         * 1. 在Panels文件夹里创建一个新窗口，把新窗口的父类从Form改为DockContent
         *    注意引用：using WeifenLuo.WinFormsUI.Docking;
         * 2. 添加下面的代码：
         *      // 下面的内容是每个面板必须要有的
                // *********** BEGIN ************* //
                // “发送AT指令”事件
                public delegate void ExistATCmdToSendHandler(ATCmd cmd);
                public event ExistATCmdToSendHandler SendATCmd;

                // 处理AT指令
                public void HandleATCmd(ATCmd cmd)
                {

                }
                // *********** END ************* //
         *  3. 在MainForm.cs里创建窗口的变量
         *  4. 在CreateAndShowPanel函数里实例化窗口并展示
         *  5. 在MainForm_Load的 1 区里绑定HandleATCmd函数
         *  6. 在MainForm_Load的 2 区里绑定SendATCmd事件
         *  7. 在GetContentFromPersistString函数中添加该窗口
         */

        // 尝试加载默认布局
        private void TryLoadDefaultLayout()
        {
            string defaultLayoutFilePath = Path.Combine(Application.StartupPath, "default_layout.xml");
            if (File.Exists(defaultLayoutFilePath))
            {
                LoadLayoutFromXML(defaultLayoutFilePath);
            }
        }

        // 主窗口加载回调函数
        private void MainForm_Load(object sender, EventArgs e)
        {
            CreatePanels();

            // 创建AT指令解析器
            atCommandParser = new ATCommandParser();
            atCommandParser.ReceivedATCmd += AtCommandParser_ReceivedATCmd;
            
            BindEvents();
            TryLoadDefaultLayout();

        }

        // 获取主串口
        private void GetMainSerial()
        {
            // 从串口终端面板获取串口对象
            mainSerialPort = serialPortTerminalPanel.GetSerialPort();

            mainSerialPort.DataReceived += MainSerialPort_DataReceived;

            // 创建并启动主串口发送任务
            mainSerialATCmdQueue = new ConcurrentQueue<ATCmd>();
            mainSerialSendTask = new Task(MainSerialSendTask);
            mainSerialSendTask.Start();
        }
        
        // 绑定各种事件
        private void BindEvents()
        {
            // 添加各个面板的AT指令处理函数
            // 每个面板的AT指令处理函数都叫HandleATCmd

            /* BEGIN 1【AT指令广播】*/

            atCommandParser.ReceivedATCmd += emergencyStopPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += serialPortTerminalPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += dataDisplayPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += roboticArmPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += chassisPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += chassisJibPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += cameraPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += impactWarningPanel.HandleATCmd;
            atCommandParser.ReceivedATCmd += attitudePanel.HandleATCmd;

            /* END 1【AT指令广播】*/

            // 绑定每个面板的AT指令发送事件函数(SendATCmd)
            /* BEGIN 2【绑定AT指令发送事件】 */

            emergencyStopPanel.SendATCmd += SendATCmdFromMainSerial;
            serialPortTerminalPanel.SendATCmd += SendATCmdFromMainSerial;
            dataDisplayPanel.SendATCmd += SendATCmdFromMainSerial;
            roboticArmPanel.SendATCmd += SendATCmdFromMainSerial;
            chassisPanel.SendATCmd += SendATCmdFromMainSerial;
            chassisJibPanel.SendATCmd += SendATCmdFromMainSerial;
            cameraPanel.SendATCmd += SendATCmdFromMainSerial;
            impactWarningPanel.SendATCmd += SendATCmdFromMainSerial;
            attitudePanel.SendATCmd += SendATCmdFromMainSerial;

            /* END 2【绑定AT指令发送事件】 */
        }

        // 主窗口发送任务函数体
        private void MainSerialSendTask()
        {
            ATCmd cmd = new ATCmd();
            string str = "";
            // 主循环
            while(mainSerialSendTaskRunning)
            {
                if (mainSerialPort == null) continue;
                if (!mainSerialPort.IsOpen) continue;

                if (!mainSerialATCmdQueue.IsEmpty)
                {
                    mainSerialATCmdQueue.TryDequeue(out cmd);
                    
                    switch(cmd.type)
                    {
                        case ATCmdType.SET:
                            str = string.Format("AT+{0}={1}\n", cmd.target, cmd.value);
                            break;
                        case ATCmdType.GET:
                            str = string.Format("AT+{0}?\n", cmd.target); 
                            break;
                        case ATCmdType.EXEC:
                            str = string.Format("AT+{0}\n", cmd.target);
                            break;
                    }

                    mainSerialPort.Write(str);
                }
            }
        }

        private void SendATCmdFromMainSerial(ATCmd cmd)
        {
            if (mainSerialPort == null || !mainSerialPort.IsOpen)
            {
                MessageBox.Show("请先打开串口", "串口错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            mainSerialATCmdQueue?.Enqueue(cmd);
        }

        //创建并展示各个面板
        private void CreatePanels()
        {
            dockPanel1.Theme = new VS2015LightTheme();
            // 创建面板
            emergencyStopPanel = new EmergencyStopPanel();
            serialPortTerminalPanel = new SerialPortTerminalPanel();
            dataDisplayPanel = new DataDisplayPanel();
            roboticArmPanel = new RoboticArmPanel();
            chassisPanel = new ChassisPanel();
            chassisJibPanel = new ChassisJibPanel();
            cameraPanel = new CameraPanel();
            impactWarningPanel = new ImpactWarningPanel();
            attitudePanel = new AttitudePanel();
        }

        // AT指令解析器回调函数
        private void AtCommandParser_ReceivedATCmd(ATCmd cmd)
        {
            // 当AT指令解析器解析出一个AT指令后就会调用这个函数

            // Console.WriteLine(string.Format("{0}: T={1} V={2}", cmd.type, cmd.target, cmd.value));
        }

        // 主串口接收数据回调函数
        private void MainSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort serialPort = (SerialPort)sender;
            int bytesToRead = serialPort.BytesToRead;
            byte[] buffer = new byte[bytesToRead];
            string str;

            serialPort.Read(buffer, 0, bytesToRead);

            str = Encoding.Default.GetString(buffer);
            serialPortTerminalPanel.AppendString(str);

            // 把接收到的数据送入at指令解析器
            foreach (byte b in buffer)
            {
                atCommandParser.Next((char)b);
            }
        }

        private void SaveLayoutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string filename;

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                filename = saveFileDialog1.FileName;
                dockPanel1.SaveAsXml(filename);
            }
        }

        private IDockContent GetContentFromPersistString(string str)
        {
            if (str == typeof(ChassisJibPanel).ToString())
            {
                return chassisJibPanel;
            }
            else if (str == typeof(ChassisPanel).ToString())
            {
                return chassisPanel;
            }
            else if (str == typeof(DataDisplayPanel).ToString())
            {
                return dataDisplayPanel;
            }
            else if (str == typeof(EmergencyStopPanel).ToString())
            {
                return emergencyStopPanel;
            }
            else if (str == typeof(RoboticArmPanel).ToString())
            {
                return roboticArmPanel;
            }
            else if (str == typeof(SerialPortTerminalPanel).ToString())
            {
                return serialPortTerminalPanel;
            }
            else if (str == typeof(CameraPanel).ToString())
            {
                return cameraPanel;
            }
            else if (str == typeof(ImpactWarningPanel).ToString())
            {
                return impactWarningPanel;
            }
            else if (str == typeof(AttitudePanel).ToString())
            {
                return attitudePanel;
            }
            else
            {
                return null;
            }
        }

        private void LoadLayoutFromXML(string xmlPath)
        {
            CreatePanels();

            BindEvents();
            GetMainSerial();


            dockPanel1.LoadFromXml(xmlPath, GetContentFromPersistString);
        }

        private void LoadLayoutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string filename;

            if (openFileDialog1.ShowDialog() == DialogResult.OK) {
                filename = openFileDialog1.FileName;
                LoadLayoutFromXML(filename);
            }
        }

        private void ChassisJibPanelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (chassisJibPanel == null)
                chassisJibPanel = new ChassisJibPanel();
            chassisJibPanel.Show(dockPanel1);
        }

        private void ChassisPanelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (chassisPanel == null)
                chassisPanel = new ChassisPanel();
            chassisPanel.Show(dockPanel1);
        }

        private void DataDisplayPanelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (dataDisplayPanel == null)
                dataDisplayPanel = new DataDisplayPanel();
            dataDisplayPanel.Show(dockPanel1);
        }

        private void EmergencyStopPanelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (emergencyStopPanel == null)
                emergencyStopPanel = new EmergencyStopPanel();
            emergencyStopPanel.Show(dockPanel1);
        }

        private void RoboticArmToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (roboticArmPanel == null)
                roboticArmPanel = new RoboticArmPanel();
            roboticArmPanel.Show(dockPanel1);
        }

        private void SerialTerminalToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (serialPortTerminalPanel == null)
                serialPortTerminalPanel = new SerialPortTerminalPanel();
            serialPortTerminalPanel.Show(dockPanel1);
            mainSerialPort = serialPortTerminalPanel.GetSerialPort();
        }

        private void SaveAsDefaultLayoutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string defaultLayoutFilePath = Path.Combine(Application.StartupPath, "default_layout.xml");
            dockPanel1.SaveAsXml(defaultLayoutFilePath);
        }

        private void CameraToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (cameraPanel == null)
                cameraPanel = new CameraPanel();
            cameraPanel.Show(dockPanel1);
        }

        private void ImpactWarningToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (impactWarningPanel == null)
                impactWarningPanel = new ImpactWarningPanel();
            impactWarningPanel.Show(dockPanel1, DockState.Float);
        }

        private void AttitudeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (attitudePanel == null)
                attitudePanel = new AttitudePanel();
            attitudePanel.Show(dockPanel1, DockState.Float);
        }

        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            mainSerialSendTaskRunning = false;
        }
    }
}
